package com.heima.search.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.heima.feign.api.article.ArticleFeignClient;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dto.PageSearchArticleDto;
import com.heima.model.search.dto.UserSearchDto;
import com.heima.model.search.pojo.SearchArticleVo;
import com.heima.search.service.ApArticleSearchService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ApArticleSearchServiceImpl implements ApArticleSearchService {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private ArticleFeignClient articleFeignClient;

    // 索引名称
    private static final String INDEX_NAME = "app_article_info";

    @Override
    public void saveArticleToES(SearchArticleVo searchArticleVo) {
        try {
            // 抽取提示词数据
            if (searchArticleVo.getSuggest() == null || searchArticleVo.getSuggest().isEmpty()) {
                searchArticleVo.setSuggest(Arrays.asList(searchArticleVo.getTitle()));
            }

            // 准备文档数据
            Map<String, Object> data = new HashMap<>();
            data.put("id", searchArticleVo.getId());
            data.put("title", searchArticleVo.getTitle());
            data.put("publishTime", searchArticleVo.getPublishTime());
            data.put("layout", searchArticleVo.getLayout());
            data.put("images", searchArticleVo.getImages());
            data.put("authorId", searchArticleVo.getAuthorId());
            data.put("authorName", searchArticleVo.getAuthorName());
            data.put("staticUrl", searchArticleVo.getStaticUrl());
            data.put("content", searchArticleVo.getContent());
            data.put("suggestion", searchArticleVo.getSuggest());

            // 创建索引请求
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME).id(searchArticleVo.getId().toString());
            indexRequest.source(data);

            // 执行索引操作
            restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            log.info("文章索引更新成功，文章ID: {}", searchArticleVo.getId());
        } catch (IOException e) {
            log.error("文章索引更新失败，文章ID: {}", searchArticleVo.getId(), e);
        }
    }
    @Override
    public List<Map<String, Object>> search(UserSearchDto dto) throws IOException {
        // 检查参数
        if (dto == null || dto.getSearchWords() == null) {
            throw new RuntimeException(AppHttpCodeEnum.PARAM_REQUIRE.getErrorMessage());
        }

        // 设置默认值
        if (dto.getPageNum() <= 0) {
            dto.setPageNum(1);
        }
        if (dto.getPageSize() <= 0) {
            dto.setPageSize(10);
        }

        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建查询条件
        searchSourceBuilder.query(QueryBuilders.multiMatchQuery(dto.getSearchWords(), "title", "content"));

        // 设置分页
        searchSourceBuilder.from(dto.getFromIndex());
        searchSourceBuilder.size(dto.getPageSize());

        // 设置排序
        if (dto.getMinBehotTime() != null) {
            searchSourceBuilder.sort("publishTime", SortOrder.ASC);
        } else {
            searchSourceBuilder.sort("publishTime", SortOrder.DESC);
        }

        // 设置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.field("content");
        highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);

        // 设置超时时间
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        searchRequest.source(searchSourceBuilder);

        // 执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        // 处理搜索结果
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();

            // 处理高亮
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields != null && !highlightFields.isEmpty()) {
                HighlightField titleField = highlightFields.get("title");
                if (titleField != null) {
                    Text[] fragments = titleField.fragments();
                    StringBuilder sb = new StringBuilder();
                    for (Text fragment : fragments) {
                        sb.append(fragment);
                    }
                    sourceAsMap.put("h_title", sb.toString());
                }

                HighlightField contentField = highlightFields.get("content");
                if (contentField != null) {
                    Text[] fragments = contentField.fragments();
                    StringBuilder sb = new StringBuilder();
                    for (Text fragment : fragments) {
                        sb.append(fragment);
                    }
                    sourceAsMap.put("h_content", sb.toString());
                }
            }

            resultList.add(sourceAsMap);
        }

        return resultList;
    }

    @Override
    public List<String> searchSuggestion(String searchWords) throws IOException {
        log.info("开始执行搜索建议，关键词: {}", searchWords);

        // 创建搜索请求
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 使用match_phrase_prefix查询实现搜索建议
        searchSourceBuilder.query(QueryBuilders.matchPhrasePrefixQuery("title", searchWords).maxExpansions(10));
        searchSourceBuilder.size(10);

        searchRequest.source(searchSourceBuilder);

        // 执行搜索
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        // 解析结果
        List<String> suggestions = new ArrayList<>();
        for (SearchHit hit : response.getHits().getHits()) {
            Map<String, Object> source = hit.getSourceAsMap();
            if (source.containsKey("title")) {
                suggestions.add((String) source.get("title"));
            }
        }

        log.info("搜索建议结果数量: {}", suggestions.size());
        log.info("搜索建议结果: {}", suggestions);

        return suggestions;
    }

    /**
     * 批量导入文章数据
     */
    @Override
    public void importData() throws IOException {
        int currentPage = 1,size=100;
        boolean isNext=true;
        while (isNext){//currentPage>总页数
            //分页查询
            PageSearchArticleDto pageInfo = articleFeignClient.pageData(currentPage,size);
            List<SearchArticleVo> list = pageInfo.getArticleVos();
            if(CollectionUtil.isEmpty(list)){
                break;
            }
            //1、创建BulkRequest   WriteRequest.RefreshPolicy.IMMEDIATE：刷新策略立即刷新
            BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);

            for (SearchArticleVo articleVo : list) {
                //2.创建Request，并添加到BulkRequest
                IndexRequest request = new IndexRequest("app_article_info").id(articleVo.getId().toString());

                //3.封装DSL语句
                articleVo.setSuggest(Arrays.asList(articleVo.getTitle()));
                request.source(JSONUtil.toJsonStr(articleVo), XContentType.JSON);

                //并添加到BulkRequest
                bulkRequest.add(request);
            }
            //4.执行请求
            restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);

            currentPage++;
            if(currentPage>pageInfo.getTotalPages()){
                isNext=false;
            }
        }
    }
}
